#ifndef __Y64_MEMORY_H__
#define __Y64_MEMORY_H__

#include "lib.h"

#define PTR_PER_PAGE    512
#define PAGE_OFFSET ((unsigned long)0xffff800000000000)
#define PAGE_GDT_SHIFT  39
#define PAGE_1G_SHIFT   30
#define PAGE_2M_SHIFT   21
#define PAGE_4K_SHIFT   12

// 内存页大小
#define PAGE_2M_SIZE    (1UL << PAGE_2M_SHIFT)
#define PAGE_4K_SIZE    (1UL << PAGE_4K_SHIFT)

// 通过下面宏定义可以方便获取内存页起始地址
#define PAGE_2M_MASK    (~(PAGE_2M_SIZE - 1))
#define PAGE_4K_MASK    (~(PAGE_4K_SIZE - 1))

// 按页对齐，向上取整
#define PAGE_2M_ALIGN(addr) (((unsigned long)(addr) + PAGE_2M_SIZE - 1) & PAGE_2M_MASK)
#define PAGE_4K_ALIGN(addr) (((unsigned long)(addr) + PAGE_4K_SIZE - 1) & PAGE_4k_MASK)

// 物理地址和虚拟地址相互转换
#define Virt_To_Phy(addr) ((unsigned long)(addr) - PAGE_OFFSET)
#define Phy_To_Virt(addr) ((unsigned long *)((unsigned long)(addr) + PAGE_OFFSET))

#define Virt_To_2M_Page(kaddr) (g_mem_mgmt.pages_struct + (Virt_To_Phy(kaddr) >> PAGE_2M_SHIFT))
#define Phy_To_2M_Page(kaddr) (g_mem_mgmt.pages_struct + (unsigned long)(kaddr) >> PAGE_2M_SHIFT)

typedef struct { unsigned long pml4t; } pml4t_t;

typedef struct { unsigned long pdpt; } pdpt_t;

typedef struct { unsigned long pdt; } pdt_t;

typedef struct { unsigned long pt; } pt_t;

/// page table attribute

// bit 63   Exception Disable:
#define PAGE_XD             (1UL << 63)
// bit 12   Page Attribute Table
#define PAGE_PAT            (1UL << 12)
// bit 8    Global Page:1-global;0-part
#define PAGE_GLOBAL         (1UL << 8)
// bit 7    Page Size:1-Big page;0-Small page
#define PAGE_PS             (1UL << 7)
// bit 6    Dirty:1-dirty;0-clean
#define PAGE_DIRTY          (1UL << 6)
// bit 5    Accessed:1-visited;0-unvisited
#define PAGE_ACCESSED       (1UL << 5)
// bit 4    Page Level Cache Disable
#define PAGE_PCD            (1UL << 4)
// bit 3    Page Level Write Through
#define PAGE_PWT            (1UL << 3)
// bit 2    User Supervisor:1-user and supervisor;0-supervisor
#define PAGE_U_S            (1UL << 2)
// bit 1    Read Write:1-read and write;0-read
#define PAGE_R_W            (1UL << 1)
// bit 0    Present:1-present;0-no present
#define PAGE_PRESENT        (1UL << 0)

// 1,0
#define PAGE_KERNEL_GDT     (PAGE_R_W | PAGE_PRESENT)
// 1,0
#define PAGE_KERNEL_DIR     (PAGE_R_W | PAGE_PRESENT)
// 7,1,0
#define PAGE_KERNEL_PAGE    (PAGE_PS | PAGE_R_W | PAGE_PRESENT)
// 2,1,0
#define PAGE_USER_DIR       (PAGE_U_S | PAGE_R_W | PAGE_PRESENT)
// 7,2,1,0
#define PAGE_USER_PAGE      (PAGE_PS | PAGE_U_S | PAGE_R_W | PAGE_PRESENT)

#define MK_MPL4T(addr, attr)    ((unsigned long)(addr) | (unsigned long)(attr))
#define SET_MPL4T(mpl4tptr, mpl4tval)   (*(mpl4tptr) = (mpl4tval))

#define MK_PDPT(addr, attr)     ((unsigned long)(addr) | (unsigned long)(attr))
#define SET_PDPT(pdptptr, pdptval)      (*(pdptptr) = (pdptval))

#define MK_PDT(addr, attr)      ((unsigned long)(addr) | (unsigned long)(attr))
#define SET_PDT(pdtptr, pdtval)         (*(pdtptr) = (pdtval))

#define MK_PT(addr, attr)       ((unsigned long)(addr) | (unsigned long)(attr))
#define SET_PT(pdptr, ptval)            (*(ptptr) = (ptval))


// alloc_pages zone_select
#define ZONE_DMA            (1 << 0)
#define ZONE_NORMAL         (1 << 1)
#define ZONE_UNMAPPED       (1 << 2)

extern int g_zone_dma_index;   
extern int g_zone_normal_index;
extern int g_zone_unmapped_index;

// struct page attribute (alloc_pages flags)
/*      位属性                      置位                            复位
    PG_PTABLE_MAPPED            已在页表中映射                  未在页表中映射
    PG_KERNEL_INIT              内核初始化程序                  非内核初始化程序
    PG_DEVICE                   设备寄存器/内存                 物理内存空间
    PG_KERNEL                   内核层地址空间                  应用层地址空间
    PG_SHARED                   已被共享的内存页                未被共享的内存页
*/
#define PG_PTABLE_MAPPED    (1 << 0)
#define PG_KERNEL_INIT      (1 << 1)
#define PG_DEVICE           (1 << 2)
#define PG_KERNEL           (1 << 3)
#define PG_SHARED           (1 << 4)

#define SET_PAGE_BITSMAP(page) *(g_mem_mgmt.bits_map + ((page->phy_addr >> PAGE_2M_SHIFT) >> 6)) |= 1UL << (page->phy_addr >> PAGE_2M_SHIFT) % 64
#define CLEAR_PAGE_BITSMAP(page) *(g_mem_mgmt.bits_map + ((page->phy_addr >> PAGE_2M_SHIFT) >> 6)) &= ~(1UL << (page->phy_addr >> PAGE_2M_SHIFT) % 64)

#define SIZEOF_LONG_ALIGN(size) ((size + sizeof(long) - 1) & ~(sizeof(long) - 1))
#define SIZEOF_INT_ALIGN(size) ((size + sizeof(int) - 1) & ~(sizeof(int) - 1))

extern unsigned long *g_global_cr3;

/*
loader程序通过INT15,AX=E820h获取到内存信息，并保存到0x7e00处，结构如下
*/
typedef struct __attribute__((packed)) {
    unsigned long address;
    unsigned long length;
    unsigned int type;                              // 1-可用物理内存,2-保留或无效值（包括ROM、设备内存）,3-ACPI的回收内存,4-ACPINVS内存
} st_mem_E820;   // 确保结构体不会字节对齐

typedef struct __st_page st_page;
typedef struct __st_global_mem_desc st_global_mem_desc;

typedef struct {
    st_page *pages_group;                    // 本区域包含的page结构体数组指针
    unsigned long pages_len;                        // 本区域包含的page结构体数量
    unsigned long zone_start_addr;                  // 本区域的起始页对齐地址（可以从page_group中拿到？）
    unsigned long zone_end_addr;                    // 本区域的结束页对齐地址（可以从page_group中拿到？）
    unsigned long zone_len;                         // 本区域经过页对齐后的地址长度
    unsigned long attr;                             // 本区域空间的属性，是否支持DMA、页是否经过页表映射等信息
    st_global_mem_desc *gmd_struct;          // 指向全局内存管理结构体
    unsigned long page_using_cnt;                   // 本区域已经使用的物理内存页数量
    unsigned long page_free_cnt;                    // 本区域空闲物理内存页数量
    unsigned long total_pages_link;                 // 本区域物理页被引用次数，一个物理页可以同时映射到线性空间多个位置上，因此和page_using_cnt不一定相等
} st_zone;

typedef struct __st_page {
    st_zone *zone_struct;                    // 指向本页所属的区域结构体
    unsigned long phy_addr;                         // 页的物理地址
    unsigned long attr;                             // 页的属性，映射状态、活动状态、使用者等信息
    unsigned long ref_cnt;                          // 页的引用次数
    unsigned long age;                              // 页的创建时间
} st_page;

typedef struct __st_global_mem_desc {
    st_mem_E820 e820[32];                    // 物理内存段结构体数组
    unsigned long e820_len;                         // 物理内存段结构体数组长度

    unsigned long *bits_map;                        // 物理地址空间页映射位图
    unsigned long bits_size;                        // 物理地址空间页数量
    unsigned long bits_len;                         // 物理地址空间页映射位图长度

    st_page *pages_struct;                   // 指向全局page结构体数组的指针
    unsigned long pages_size;                       // page结构体总数
    unsigned long pages_len;                        // page结构体数组长度

    st_zone *zones_struct;                   // 指向全局zone结构体数组的指针
    unsigned long zones_size;                       // zone结构体数量
    unsigned long zones_len;                        // zone结构体数组长度

    unsigned long start_code, end_code, end_data, end_brk;  // 内核程序的起始代码段地址、结束代码段地址、结束数据段地址、内核程序结束地址，在Kernel.lds中定义了，如_text
    unsigned long end_of_struct;                    // 内存页管理结构的结尾地址
} st_global_mem_desc;

extern st_global_mem_desc g_mem_mgmt;
// slab对象，管理具体内存对象
typedef struct {
    st_list list;
    st_page *page;
    unsigned long using_cnt;
    unsigned long free_cnt;
    void *vir_addr;                                 // 线性地址
    unsigned long color_len;
    unsigned long color_cnt;
    unsigned long *color_map;
} st_slab;
// slab内存池，管理整体内存池
typedef struct {
    unsigned long size;
    unsigned long total_using;
    unsigned long total_free;
    st_slab *cache_pool;
    st_slab *cache_dma_pool;
    void *(* constructor)(void *vir_addr, unsigned long arg);
    void *(* destructor)(void *vir_addr, unsigned long arg);
} st_slab_cache;

extern st_slab_cache g_kmalloc_cache_size[16];

void memory_init();

inline unsigned long *get_gdt();                    // 获取cr3控制寄存器的值

// 重新赋值cr3寄存器，以使更改后的页表生效
#define FLUSH_TLB()     \
do {    \
    unsigned long tmpreg;   \
    __asm__ __volatile__ (  \
        "movq %%cr3, %0 \n\t"   \
        "movq %0, %%cr3 \n\t"   \
        :"=r"(tmpreg)   \
        :   \
        :"memory"   \
    );  \
} while(0)

// 最多申请连续的64个内存页
st_page *alloc_pages(int zone_select, int number, unsigned long page_flags);

st_slab_cache *slab_create(unsigned long size, void *(* constructor)(void *vir_addr, unsigned long arg), void *(* destructor)(void *vir_addr, unsigned long arg), unsigned long arg);

void free_pages(st_page *page, int number);

unsigned long slab_init();

void *kmalloc(unsigned long size, unsigned long gfp_flags);

st_slab *kmalloc_create(unsigned long size);

unsigned long kfree(void *addr);

void pagetable_init();

#endif